﻿Friend Class Handler_OpenMediaInfo
    Friend Class Result
        Public OmxFilePath As String
        Public MediaInfo As Entities.MediaInfo
    End Class

    Friend Shared Function CreateOrUpdateMediaInfo(ByVal item As Entities.SharedItem, ByVal mode As CreateMediaInfoMode, ByVal imdbHintUri As System.Uri) As Result
        'at the moment, only movies are supported
        Select Case item.ItemType
            Case Entities.ItemType.Movies
            Case Else
                Return Nothing
        End Select

        'ok, so start by checking if the item has any media info stored in the database
        Dim clsCurrentMI As Entities.MediaInfo = EntityHandlers.MediaInfo.GetBySharedItemId(item.Id)

        'get the files for the item
        Dim aFiles As Generic.List(Of Entities.SharedItemFile)
        aFiles = EntityHandlers.SharedItemsFiles.GetAllBySharedItemId(item.Id)

        'now, read or/create the best source of feature information
        Dim clsFeatureInfo As Entities.OpenMediaInfo.FeatureInfo = GetCurrentFeatureInfo(clsCurrentMI, item, aFiles, imdbHintUri, mode = CreateMediaInfoMode.ForceHintImdbUriAsFeature)
        'and to the same for stream information
        Dim aStreamInfos As Generic.List(Of Entities.OpenMediaInfo.StreamInfo) = GetCurrentStreamInfos(clsCurrentMI, item, aFiles)

        Dim clsOMI As New Entities.OpenMediaInfo.OpenMediaInfo
        clsOMI.Feature = clsFeatureInfo
        clsOMI.Streams = aStreamInfos
        clsOMI.Version = OpenMediaInfo.Manager.CurrentOmxVersion

        Dim clsNewMI As New Entities.MediaInfo
        clsNewMI.OpenMediaInfo = clsOMI
        clsNewMI.SharedItemId = item.Id
        If clsFeatureInfo IsNot Nothing Then
            clsNewMI.FeatureInfoStatus = Entities.FeatureInfoStatus.Present
            clsNewMI.Genres = New Generic.List(Of String)
            For Each clsGenre As Entities.OpenMediaInfo.Genre In clsFeatureInfo.Genres
                clsNewMI.Genres.Add(clsGenre.Name)
            Next
            clsNewMI.FeatureTitle = clsFeatureInfo.Title
        ElseIf clsCurrentMI IsNot Nothing AndAlso clsCurrentMI.FeatureInfoStatus = Entities.FeatureInfoStatus.Missing Then
            clsNewMI.FeatureInfoStatus = Entities.FeatureInfoStatus.Missing
            clsNewMI.FeatureTitle = clsCurrentMI.FeatureTitle
        ElseIf mode = CreateMediaInfoMode.TagAsFeatureNotAvailable Then
            clsNewMI.FeatureInfoStatus = Entities.FeatureInfoStatus.Missing
            clsNewMI.FeatureTitle = item.Name
        Else
            clsNewMI.FeatureTitle = item.Name
            clsNewMI.FeatureInfoStatus = Entities.FeatureInfoStatus.MissingNeedsAction
        End If

        'now, write updated file to disc
        'first, see if we have already have an omx file
        Dim strOmxFilePath As String = FindOmxFilePath(aFiles)
        If strOmxFilePath Is Nothing Then
            Dim aFilePaths As Generic.List(Of String) = GetAllFilePaths(aFiles)
            strOmxFilePath = OpenMediaInfo.Manager.GetSuggestedOmxFileName(item, aFilePaths)
        End If

        Dim strXml As String = Framework.Serialization.ToXml(clsNewMI.OpenMediaInfo)
        Using clsBlock As New FileSystemScan.RescanBlock(strOmxFilePath)
            Framework.IO.Utils.SaveTextFileUnicode(strOmxFilePath, strXml)
        End Using

        Dim clsRet As New Result
        clsRet.OmxFilePath = strOmxFilePath
        clsRet.MediaInfo = clsNewMI
        Return clsRet
    End Function

    Private Shared Function GetCurrentStreamInfos(ByVal currentMI As Entities.MediaInfo, ByVal item As Entities.SharedItem, ByVal itemFiles As Generic.List(Of Entities.SharedItemFile)) As Generic.List(Of Entities.OpenMediaInfo.StreamInfo)
        'setup return value and holders of loaded information
        Dim aRet As New Generic.List(Of Entities.OpenMediaInfo.StreamInfo)

        'ok, so start by checking if the item has any media info stored in the database
        If currentMI IsNot Nothing AndAlso currentMI.OpenMediaInfo IsNot Nothing AndAlso currentMI.OpenMediaInfo.Streams IsNot Nothing Then
            aRet = currentMI.OpenMediaInfo.Streams
            For Each clsSI As Entities.OpenMediaInfo.StreamInfo In aRet
                'if any of the stream information is manually edited, do nothing
                If clsSI.InformationSource <> Entities.OpenMediaInfo.InformationSource.Automatically Then
                    Return aRet
                End If
            Next
        End If

        Dim aFilePaths As Generic.List(Of String) = GetAllFilePaths(itemFiles)
        aRet = New Generic.List(Of Entities.OpenMediaInfo.StreamInfo)
        aRet.Add(OpenMediaInfo.MediaInfoDll.Manager.GetStreamInfo(aFilePaths))
        Return aRet
    End Function

    Private Shared Function GetAllFilePaths(ByVal files As Generic.List(Of Entities.SharedItemFile)) As Generic.List(Of String)
        Dim aFilePaths As New Generic.List(Of String)
        For Each clsFile As Entities.SharedItemFile In files
            aFilePaths.Add(IO.Path.Combine(clsFile.Directory, clsFile.FileName))
        Next
        Return aFilePaths
    End Function

    Private Shared Function GetCurrentFeatureInfo(ByVal currentMI As Entities.MediaInfo, ByVal item As Entities.SharedItem, ByVal itemFiles As Generic.List(Of Entities.SharedItemFile), ByVal imdbHintUri As System.Uri, ByVal forceUseOfHint As Boolean) As Entities.OpenMediaInfo.FeatureInfo
        'setup return value and holders of loaded information
        Dim clsRet As Entities.OpenMediaInfo.FeatureInfo = Nothing
        Dim clsDiskOMI As Entities.OpenMediaInfo.OpenMediaInfo = Nothing

        If Not forceUseOfHint AndAlso currentMI IsNot Nothing AndAlso currentMI.OpenMediaInfo IsNot Nothing AndAlso currentMI.OpenMediaInfo.Feature IsNot Nothing Then
            clsRet = currentMI.OpenMediaInfo.Feature
        End If

        'if we found feature information in the datastore and it wasn't created automatically
        'then return it since we do not overwrite information that the user created or modified
        If clsRet IsNot Nothing AndAlso clsRet.InformationSource <> Entities.OpenMediaInfo.InformationSource.Automatically Then
            Return clsRet
        End If

        Dim strImdbId As String = Nothing

        'so, force use of hint?
        If forceUseOfHint Then
            strImdbId = OpenMediaInfo.IMDB.Manager.GetImdbIdFromUri(imdbHintUri)
        End If
        If strImdbId Is Nothing Then
            'ok, do we have a IMDB link in the existing data?
            If clsRet IsNot Nothing Then strImdbId = GetImbdId(clsRet)
        End If

        If strImdbId Is Nothing Then
            'ok, so we do not know the imdb id in the datastore
            'try to get it from any omx-file in disc
            clsDiskOMI = LoadOmxFileIfPresent(itemFiles)
            If clsDiskOMI IsNot Nothing AndAlso clsDiskOMI.Feature IsNot Nothing Then
                'if we found feature info in omx-file and it wasn't automatically generated, return it!
                If clsDiskOMI.Feature.InformationSource <> Entities.OpenMediaInfo.InformationSource.Automatically Then
                    Return clsDiskOMI.Feature
                End If
                clsRet = clsDiskOMI.Feature
                strImdbId = GetImbdId(clsDiskOMI.Feature)
            End If
        End If

        If strImdbId Is Nothing AndAlso imdbHintUri IsNot Nothing Then
            'ok, did we get the imdb uri passed to us, use it!
            strImdbId = OpenMediaInfo.IMDB.Manager.GetImdbIdFromUri(imdbHintUri)
        End If

        If strImdbId Is Nothing Then
            'if we still do not have the imdb-link, try to extract it from any textfile
            'available
            strImdbId = GetImdbIdFromFiles(itemFiles)
        End If

        'now, see if IMDB will return a title based on the name of the item
        If strImdbId Is Nothing Then
            Dim strCleanTitle As String = OpenMediaInfo.IMDB.Search.CleanTitleForSearch(item.Name)

            Dim clsId As OpenMediaInfo.IMDB.Search.GetImdbIdByTitleResult
            clsId = OpenMediaInfo.IMDB.Search.GetImdbIdByTitle(strCleanTitle)
            If clsId IsNot Nothing Then strImdbId = clsId.Id

            If strImdbId Is Nothing Then
                'ok, so now we try to find it from the search result
                Dim aTitles As Generic.List(Of OpenMediaInfo.IMDB.Search.FoundTitle)
                aTitles = OpenMediaInfo.IMDB.Search.GetAllTitlesFromSearchResult(clsId.Html, OpenMediaInfo.IMDB.Search.FoundTitleType.ExactMatch)
                Dim strYear As String = FindYearInComment(item.Comment)
                For Each clsTitle As OpenMediaInfo.IMDB.Search.FoundTitle In aTitles
                    If clsTitle.Name.ToLower = strCleanTitle.ToLower Then
                        If clsTitle.Year = strYear Then
                            strImdbId = clsTitle.Id
                            Exit For
                        End If
                    End If
                Next
                If strImdbId Is Nothing Then
                    aTitles = OpenMediaInfo.IMDB.Search.GetAllTitlesFromSearchResult(clsId.Html, OpenMediaInfo.IMDB.Search.FoundTitleType.Popular)
                    For Each clsTitle As OpenMediaInfo.IMDB.Search.FoundTitle In aTitles
                        If clsTitle.Name.ToLower = item.Name.ToLower Then
                            If clsTitle.Year = strYear Then
                                strImdbId = clsTitle.Id
                                Exit For
                            End If
                        End If
                    Next
                End If
            End If
        End If



        'alright, do we have the imdbid now?
        If strImdbId IsNot Nothing Then
            Dim clsMovieUri As Uri = OpenMediaInfo.IMDB.Manager.GetUriFromId(strImdbId) ' New Uri(String.Format("http://www.imdb.com/title/{0}", strImdbId))
            FileShare.Engine.Manager.OnLogEvent(String.Format("Retrieving feature information from imdb.com for {0}...", item.Name))
            clsRet = OpenMediaInfo.IMDB.Manager.GetMovieInfo(clsMovieUri)
        End If

        'if clsret isn't nothing it holds the most current information
        If clsRet IsNot Nothing Then
            Return clsRet
        ElseIf clsDiskOMI IsNot Nothing AndAlso clsDiskOMI.Feature IsNot Nothing Then
            'ok, we fall back on the omx-file if present and containg feature information
            Return clsDiskOMI.Feature
        ElseIf currentMI IsNot Nothing AndAlso currentMI.OpenMediaInfo IsNot Nothing AndAlso currentMI.OpenMediaInfo.Feature IsNot Nothing Then
            'aight, no feature information on disk - return whatever we had in the datastore (if present)
            Return currentMI.OpenMediaInfo.Feature
        Else
            'we give up! We cannot find feature information anywhere
            Return Nothing
        End If
    End Function

    Public Shared Function FindYearInComment(ByVal text As String) As String
        If text Is Nothing Then Return Nothing
        'find all 4-digit numbers
        Dim clsSBWashed As New System.Text.StringBuilder

        For i As Integer = 0 To text.Count - 1
            If text(i) >= "0"c And text(i) <= "9" Then
                clsSBWashed.Append(text(i))
            Else
                If clsSBWashed.Length = 0 Then
                ElseIf clsSBWashed.Chars(clsSBWashed.Length - 1) <> " "c Then
                    clsSBWashed.Append(" "c)
                End If
            End If
        Next
        Dim aNums() As String = clsSBWashed.ToString.Split(" "c)
        For Each strNum As String In aNums
            Dim iYear As Integer = 0
            If Integer.TryParse(strNum, iYear) Then
                If iYear > 1890 And iYear < Now.Year + 2 Then
                    Return strNum
                End If
            End If
        Next
        Return Nothing
    End Function

    Private Shared Function GetImbdId(ByVal feature As Entities.OpenMediaInfo.FeatureInfo) As String
        If feature IsNot Nothing AndAlso feature.Ids IsNot Nothing Then
            For Each strId As String In feature.Ids
                If strId.ToLower.StartsWith("imdb:") Then
                    Return strId.Substring(5)
                End If
            Next
        End If
        Return Nothing
    End Function

    Private Shared Function FindOmxFilePath(ByVal files As Generic.List(Of Entities.SharedItemFile)) As String
        For Each clsFile As Entities.SharedItemFile In files
            If IO.Path.GetExtension(clsFile.FileName).ToLower = ".omx" Then
                Return IO.Path.Combine(clsFile.Directory, clsFile.FileName)
            End If
        Next
        Return Nothing
    End Function

    Private Shared Function LoadOmxFileIfPresent(ByVal files As Generic.List(Of Entities.SharedItemFile)) As Entities.OpenMediaInfo.OpenMediaInfo
        'first, if omx file exists, load it
        Dim strOmxFile As String = FindOmxFilePath(files)

        Dim clsMI As Entities.OpenMediaInfo.OpenMediaInfo = Nothing
        If strOmxFile IsNot Nothing Then
            Dim strXml As String = Framework.IO.Utils.ReadTextFile(strOmxFile)
            Try
                clsMI = Framework.Serialization.FromXml(Of Entities.OpenMediaInfo.OpenMediaInfo)(strXml)
            Catch ex As InvalidOperationException
                'something wrong with the document. Ignore it
                clsMI = Nothing
            Catch ex As Xml.XmlException
                'something wrong with the document. Ignore it
                clsMI = Nothing
            End Try
        End If
        Return clsMI
    End Function

    Private Shared Function GetImdbIdFromFiles(ByVal files As Generic.List(Of Entities.SharedItemFile)) As String
        Dim aUri As Uri = FindImdbLinkInFiles(files)
        If aUri Is Nothing Then
            Return Nothing
        Else
            Return OpenMediaInfo.IMDB.Manager.GetImdbIdFromUri(aUri)
        End If
    End Function

    Private Shared Function FindImdbLinkInFiles(ByVal files As Generic.List(Of Entities.SharedItemFile)) As Uri
        Dim aPrefixes() As String = New String() {"http://", "www.imdb.com/", "imdb.com/"}
        Dim clsRet As Uri = Nothing
        For Each clsFile As Entities.SharedItemFile In files
            Dim strFilePath As String = IO.Path.Combine(clsFile.Directory, clsFile.FileName)
            Select Case IO.Path.GetExtension(strFilePath).ToLower
                Case ".nfo", ".txt", ".info", ".text"
                    Dim sText As String = Framework.IO.Utils.ReadTextFile(strFilePath)
                    Dim aUris As Generic.List(Of Uri) = GetAllLinksInText(sText, aPrefixes)
                    For Each clsUri As Uri In aUris
                        If clsUri.Host.ToLower.EndsWith("imdb.com") Then
                            If clsUri.PathAndQuery.ToLower.Contains("/title/tt") Then
                                Return clsUri
                            End If
                        End If
                    Next
            End Select
        Next
        Return Nothing
    End Function
    Private Shared Function GetAllLinksInText(ByVal text As String, ByVal linkPrefixes() As String) As Generic.List(Of Uri)
        Dim aRet As New Generic.List(Of Uri)
        Dim strTmpText As String = text.ToLower
        Dim iPos As Integer = FindStartOfNextLink(text, 0, linkPrefixes)
        While iPos >= 0
            Dim iEndPos As Integer = FindEndOfLink(text, iPos)
            Dim strUrl As String = text.Substring(iPos, iEndPos - iPos)
            If Uri.IsWellFormedUriString(strUrl, UriKind.Absolute) Then
                aRet.Add(New Uri(strUrl))
            ElseIf Uri.IsWellFormedUriString("http://" & strUrl, UriKind.Absolute) Then
                aRet.Add(New Uri("http://" & strUrl))
            ElseIf Uri.IsWellFormedUriString("http:/" & strUrl, UriKind.Absolute) Then
                aRet.Add(New Uri("http:/" & strUrl))
            End If
            iPos = FindStartOfNextLink(text, iEndPos, linkPrefixes)
        End While
        Return aRet
    End Function
    Private Shared Function FindStartOfNextLink(ByVal text As String, ByVal start As Integer, ByVal prefixes() As String) As Integer
        Dim iStart As Integer = -1
        For Each strPrefix As String In prefixes
            Dim iPos As Integer = text.IndexOf(strPrefix, start)
            If iPos >= 0 AndAlso (iStart = -1 Or iPos < iStart) Then iStart = iPos
        Next
        Return iStart
    End Function
    Private Shared Function FindEndOfLink(ByVal text As String, ByVal start As Integer) As Integer
        Dim iPos As Integer = text.IndexOfAny(New Char() {" "c, vbCr.Chars(0), vbLf.Chars(0)}, start)
        If iPos < start Then iPos = text.Length
        If text.Chars(iPos - 1) = "."c Then iPos -= 1
        Return iPos
    End Function


End Class
